﻿#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEditor.U2D;
using UnityEngine;
/// <summary>
/// AssetBundle资源包选择资源项
/// </summary>
public class ESF_CollectAsset_AssetBundle : AbsESF_EditorCollectAsset
{
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="_pathOrGuid">资源路径或guid</param>
    public ESF_CollectAsset_AssetBundle(string _pathOrGuid) : base(_pathOrGuid)
    {
        mRefrenceNodes.Clear();
        mDependencyNodes.Clear();
        assetBundleName = string.Empty;
        assetBundleKey = path.ESF_AssetBundleKey();
    }

    /// <summary>
    /// 资源包名称
    /// </summary>
    public string assetBundleName { get; private set; }
    /// <summary>
    /// 资源包Key
    /// </summary>
    public int assetBundleKey { get; private set; }

    /// <summary>
    /// 是否是强制收缩的根节点
    /// </summary>
    public bool isRootForuceShrinkMerge { get; private set; }

    #region IsAllowSetAssetBundle 是否允许设置AssetBundle名称
    /// <summary>
    /// 是否允许设置AssetBundle名称
    /// </summary>
    /// <param name="_filePath">文件名</param>
    /// <returns>true:允许设置AssetBundle名称,false:不允许设置AssetBundle名称</returns>
    public bool IsAllowSetAssetBundle(string _filePath)
    {
        bool result = File.Exists(_filePath);
        if (isFile)
        {
            result &= ESF_Logic.assetBundleSchema.xml.IsAllowSetAssetBundle(Path.GetExtension(_filePath).ESF_ToUnityFileExt());
            result &= ESF_Logic.assetBundleSchema.xml.IsAllowSetAssetBundle(Path.GetFileName(_filePath));
            result &= !ESF_Utility.HasBelongEditorDirectory(_filePath);
            result &= !ESF_Utility.IsIllegalFile(Path.GetFileNameWithoutExtension(_filePath));
            result &= !ESF_Utility.IsIllegalDirectory(Path.GetDirectoryName(_filePath));
        }
        return result;
    }

    /// <summary>
    /// 是否允许设置AssetBundle名称
    /// </summary>
    /// <returns>true:允许设置AssetBundle名称,false:不允许设置AssetBundle名称</returns>
    public bool IsAllowSetAssetBundle()
    {
        bool result = isFile;
        if (isFile)
        {
            result &= ESF_Logic.assetBundleSchema.xml.IsAllowSetAssetBundle(ext.ESF_ToUnityFileExt());
            result &= ESF_Logic.assetBundleSchema.xml.IsAllowSetAssetBundle(name);            
            result &= !ESF_Utility.HasBelongEditorDirectory(path);
            result &= !ESF_Utility.IsIllegalFile(nameWithoutExtension);
            result &= !ESF_Utility.IsIllegalDirectory(directory);
        }
        return result;
    }
    #endregion

    #region BuildDependency 构建节点依赖链
    /// <summary>
    /// 引用当前资源的资源节点
    /// </summary>
    List<string> mRefrenceNodes = new List<string>();
    /// <summary>
    /// 当前资源的依赖资源节点
    /// </summary>
    List<string> mDependencyNodes = new List<string>();
    /// <summary>
    /// 场后缀属性
    /// </summary>
    ESF_FileExtAttribute mSceneExtAttribute = enESF_UnityFileExt.Scene.ESF_FirstAttribute<ESF_FileExtAttribute>();
    /// <summary>
    /// 构建节点依赖链
    /// </summary>
    /// <param name="_topologyLink">拓扑资源链</param>
    public void BuildDependency(Dictionary<string, ESF_CollectAsset_AssetBundle> _topologyLink)
    {
        if (!_topologyLink.ContainsKey(path))
        {//如果没有构建过此节点的依赖项则解析构建
            if (IsAllowSetAssetBundle(path))
            {
                _topologyLink.Add(path, this);  
                string[] deps = AssetDatabase.GetDependencies(path, false);
                if (deps != null && deps.Length > 0)
                {
                    ESF_CollectAsset_AssetBundle tempAb = null;
                    foreach (var dep in deps)
                    {
                        if (
                            !dep.Equals(path)
                            && !mSceneExtAttribute.IsExt(Path.GetExtension(dep))
                            && IsAllowSetAssetBundle(dep)
                            )
                        {
                            if (!_topologyLink.TryGetValue(dep, out tempAb))
                            {
                                tempAb = new ESF_CollectAsset_AssetBundle(dep);
                            }
                            if (tempAb.IsAllowSetAssetBundle())
                            {
                                if (!tempAb.mRefrenceNodes.Contains(path))
                                {
                                    tempAb.mRefrenceNodes.Add(path);
                                }
                                if (!mDependencyNodes.Contains(dep))
                                {
                                    mDependencyNodes.Add(dep);
                                }
                                tempAb.BuildDependency(_topologyLink);
                            }
                        }
                    }
                }
            }
        }
    }
    public List<string> GetDependencyNodes() {
        return mDependencyNodes;
    }
    #endregion

    #region FilterDependency 过滤依赖项
    /// <summary>
    /// 过滤依赖项
    /// </summary>
    /// <param name="_topologyLink">拓扑链</param>
    public void FilterDependency(Dictionary<string, ESF_CollectAsset_AssetBundle> _topologyLink)
    {
        List<string> removes = new List<string>();
        foreach (var key in mDependencyNodes)
        {
            if (_topologyLink.ContainsKey(key))
            {
                if (!removes.Contains(key))
                {
                    removes.Add(key);
                }
                foreach (var dep in _topologyLink[key].mDependencyNodes)
                {
                    if (!removes.Contains(dep))
                    {
                        removes.Add(dep);
                    }
                }
            }
        }
        foreach (var key in removes)
        {
            mDependencyNodes.Remove(key);
        }
    }
    #endregion

    #region ShrinkMerge 收缩合并
    #region 收缩合并说明
    /*
     * 收缩合并
     * 1.收缩合并目的是为了保证任何一个资源X都是被多个或零个资源引用，
     *    将所有只有一个资源引用的资源收缩到引用的资源内，
     *    以达到在使用任何资源时只进行最小的物理文件读取次数，加快载入内存的时间消耗。
     * 
     * 收缩合并图解:
     * 递归对所有只有唯一引用资源的资源都归入到引用资源中
     *            A       G
     *          /   \      |
     *         B    C    H                  【收缩合并】D合并到B
     *       /   \/   \  /
     *     D    E     F                   
     *===================================
     *            A        G             
     *          /    \      |            
     *     [BD]       C     H                【收缩合并】BD、C合并到A，H合并到G
     *          \   /  \  /
     *            E     F 
     *===================================
     *           A[BDC]   G[H]       【收缩合并】E合并到A
     *            |     \     /
     *           E        F
     *===================================
     *[BDCE] A    G[H]              【收缩合并】没有需要收缩合并的项，收缩合并结束
     *             \  /
     *               F                     
     *===================================
     * 结果=> 【ABDCE】=资源A,【GH】=资源G,【F】= 资源F
     * =====================
     */
    #endregion
    /// <summary>
    /// 收缩合并
    /// </summary>
    /// <param name="_topologyLink">拓扑资源链</param>
    /// <param name="_rootNodes">根节点</param>
    /// <param name="_checkedNodes">已检测的资源</param>
    /// <returns>true:当前节点被收缩,false:当前节点没有被收缩</returns>
    public bool ShrinkMerge(Dictionary<string, ESF_CollectAsset_AssetBundle> _topologyLink,
        Dictionary<string, ESF_CollectAsset_AssetBundle> _rootNodes, List<string> _checkedNodes)
    {
        bool hasShrinkMerge = false;        
        if (!_checkedNodes.Contains(path))
        {
            _checkedNodes.Add(path);
            //string spriteAtlasPath = OnFindSpriteAtlasReference(mRefrenceNodes);
            //bool isSpriteAtlasChild = !string.IsNullOrEmpty(spriteAtlasPath);     
            //if (mRefrenceNodes.Count == 1 && !OnForceAssetBundleRoot(this, _rootNodes) || isSpriteAtlasChild)
            bool isSpriteAtlasChild = false;
            string spriteAtlasPath = string.Empty;            
            if (isShrinkMerge(_rootNodes,out isSpriteAtlasChild,out spriteAtlasPath))
            {//如果当前资源被一个资源引用
             //当前资源的依赖资源收缩到引用当前资源的节点中
                ESF_CollectAsset_AssetBundle refAsset = null;
                if (isSpriteAtlasChild)
                {
                    refAsset = _topologyLink[spriteAtlasPath];
                }
                else
                {
                    refAsset = _topologyLink[mRefrenceNodes[0]];
                }                
                ESF_CollectAsset_AssetBundle depAsset = null;
                string[] deps = new string[mDependencyNodes.Count];
                mDependencyNodes.CopyTo(deps);
                foreach (var key in deps)
                {
                    if (_topologyLink.TryGetValue(key, out depAsset))
                    {
                        //依赖资源的引用资源变更为当前资源的引用资源
                        depAsset.mRefrenceNodes.Remove(path);
                        if (!depAsset.mRefrenceNodes.Contains(refAsset.path))
                        {
                            depAsset.mRefrenceNodes.Add(refAsset.path);
                        }
                        //引用资源的依赖资源添加当前资源的依赖资源
                        if (!refAsset.mDependencyNodes.Contains(key))
                        {
                            refAsset.mDependencyNodes.Add(key);
                        }
                    }
                }
                if (!refAsset.mDependencyNodes.Contains(path))
                {
                    refAsset.mDependencyNodes.Add(path);
                }
                mRefrenceNodes.Clear();
                mDependencyNodes.Clear();
                hasShrinkMerge = true;                
            }
        }
        return hasShrinkMerge;
    }

    /// <summary>
    /// 是否是SpriteAtlas资源引用的资源
    /// </summary>
    /// <param name="_refrences">引用的资源</param>
    /// <returns>引用当前资源的SpriteAtlas资源</returns>
    private string OnFindSpriteAtlasReference(List<string> _refrences)
    {
        List<string> spriteAtlas = new List<string>();
        if (_refrences != null && _refrences.Count > 0)
        {
            string ext = string.Empty;
            ESF_FileExtAttribute attr = enESF_UnityFileExt.SpriteAtlas.ESF_FirstAttribute<ESF_FileExtAttribute>();
            foreach (var key in _refrences)
            {
                ext = Path.GetExtension(key);
                if (attr.IsExt(ext) && !spriteAtlas.Contains(key))
                {
                    spriteAtlas.Add(key);
                }
            }
        }
        return spriteAtlas.Count == 1 ? spriteAtlas[0] : string.Empty;
    }

    /// <summary>
    /// 是否强制AssetBundle根资源
    /// </summary>
    /// <param name="_asset">资源</param>
    /// <param name="_rootNodes">根节点</param>
    /// <returns>true:是强制AssetBundle根资源,false:不是强制AssetBundle根资源</returns>
    private bool OnForceAssetBundleRoot(ESF_CollectAsset_AssetBundle _asset, Dictionary<string, ESF_CollectAsset_AssetBundle> _rootNodes)
    {
        return ESF_Logic.assetBundleSchema.xml.IsForceAssetBundleRoot(_asset.ext.ESF_ToUnityFileExt()) && _rootNodes.ContainsKey(_asset.path);
    }
    #endregion

    #region isShrinkMerge 是否是被收缩合并的资源
    /// <summary>
    /// 是否是被收缩合并的点
    /// </summary>
    /// <param name="_rootNodes">根资源</param>
    /// <param name="_isSpriteAtlasChild">是否是SpriteAtlas子节点</param>
    /// <param name="_spriteAtlasPath">SpriteAtlas路径</param>
    /// <returns>true:收缩合并,false:不收缩合并</returns>
    public bool isShrinkMerge(Dictionary<string, ESF_CollectAsset_AssetBundle> _rootNodes,
        out bool _isSpriteAtlasChild,out string _spriteAtlasPath)
    {
        _spriteAtlasPath = OnFindSpriteAtlasReference(mRefrenceNodes);
        _isSpriteAtlasChild = !string.IsNullOrEmpty(_spriteAtlasPath);
        bool result = mRefrenceNodes.Count == 1 && !OnForceAssetBundleRoot(this, _rootNodes) || _isSpriteAtlasChild;
        isRootForuceShrinkMerge |= result;
        return result;
    }
    #endregion

    #region SaveAssetBundleName 保存AssetBundleName    
    /// <summary>
    /// 解析AssetBundleName
    /// </summary>
    /// <param name="_abName">源AssetBundleName</param>
    /// <returns>解析后AssetBundleName</returns>
    string OnResolveAssetBundleName(string _abName)
    {
        string result = _abName;
        if (Path.GetExtension(_abName) != ESF_GlobalUtility.assetBundleNameSuffixExt.ext)
        {
            result = $"{_abName}{ESF_GlobalUtility.assetBundleNameSuffixExt.ext}";
        }
        return result;
    }
    /// <summary>
    /// 保存AssetBundleName
    /// </summary>
    public void SaveAssetBundleName()
    {
        AssetImporter importer = path.ToAssetImporter();        
        assetBundleName = path.ESF_OnlyCNUSPAndOtherReplaceU();        
        importer.assetBundleName = OnResolveAssetBundleName(assetBundleName);
        string depAbName = $"{assetBundleName}_{ESF_Logic.assetBundleSchema.xml.objectRefDepSuffix}";
        if (ext.ESF_ToUnityFileExt() == enESF_UnityFileExt.Scene)
        {
            depAbName = $"{assetBundleName}_{ESF_Logic.assetBundleSchema.xml.sceneRefDepSuffix}";
        }        
        bool isSpriteAtlas = ext.ESF_ToUnityFileExt() == enESF_UnityFileExt.SpriteAtlas;
        EditorUtility.SetDirty(importer);
        if (mDependencyNodes != null)
        {            
            foreach (var key in mDependencyNodes)
            {
                bool isSprite = false;
                importer = key.ToAssetImporter();
                TextureImporter texImp = key.ToTextureImporter();
                if (texImp != null)
                {
                    isSprite = texImp.textureType == TextureImporterType.Sprite;
                }
                if (importer is ShaderImporter)
                {
                    importer.assetBundleName = OnResolveAssetBundleName(key.ESF_OnlyCNUSPAndOtherReplaceU());
                }
                else if (!isSpriteAtlas && !isSprite)
                {//如果主资源不是SpriteAtlas并且依赖项不是Sprite资源
                    importer.assetBundleName = OnResolveAssetBundleName(depAbName);
                }
                else if(isSpriteAtlas && isSprite)
                {//主资源是SpriteAtlas，则所有依赖项变更为SpriteAtlas资源包名
                    
                    FileInfo file = new FileInfo(key);
                    DirectoryInfo dir = file.Directory;
                    int assetsLenth = Application.dataPath.Substring(0, Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length).Length).Length;
                    string assetpath = dir.FullName.Substring(assetsLenth);
                    //string bundleName = Path.GetDirectoryName(ESF_Logic.assetBundleBuild.xml.GetRelativeLangPath(assetpath)).ESF_OnlyCNUAndOtherReplaceU();
                    enESF_AllLanguageClassify lan = 0;
                    string rf = string.Empty;
                    string sub = ESF_Logic.assetBundleBuild.xml.GetRelativeLangPath(assetpath, ref lan, ref rf);
                    string enESF_LanguageRootFolderName = rf.ESF_OnlyCNUAndOtherReplaceU().ToUpper();
                    string enESF_SpriteAtlasName = Path.Combine(rf, sub).ESF_OnlyCNUAndOtherReplaceU().ToUpper();
                    string readspritesroot = dir.Parent.FullName.Substring(assetsLenth);
                    string relativepath = assetpath.Replace(readspritesroot, "").ESF_ForceRelativePath();
                    string spriteatlasnamewithoutextension = $"{enESF_SpriteAtlasName}_{lan}";
                    string spriteatlasname = spriteatlasnamewithoutextension + enESF_UnityFileExt.SpriteAtlas.ESF_FirstAttribute<ESF_FileExtAttribute>().ext;
                    string writespriteatlasroot= ESF_Logic.spriteAtlasSettingFolder.xml.spriteAtlasTargets[((int)lan)];
                    string savespriteatlaspath = Path.Combine(Path.Combine(writespriteatlasroot, relativepath), spriteatlasname).ESF_TransPathSeparatorCharToUnityChar();

                    importer.assetBundleName = OnResolveAssetBundleName(savespriteatlaspath).ESF_TransPathSeparatorCharToUnityChar();
                }
                //else if (!isSpriteAtlas)
                //{
                //    FileInfo file = new FileInfo(key);
                //    DirectoryInfo dir = file.Directory;
                //    string bundleName = Path.GetDirectoryName(ESF_Logic.assetBundleBuild.xml.GetRelativeLangPath(key)).ESF_OnlyCNUAndOtherReplaceU();
                //    enESF_AllLanguageClassify lan = 0;
                //    string rf = string.Empty;
                //    string sub = ESF_Logic.assetBundleBuild.xml.GetRelativeLangPath(bundleName, ref lan, ref rf);

                //    string enESF_LanguageRootFolderName = rf.ESF_OnlyCNUAndOtherReplaceU().ToUpper();
                //    string enESF_SpriteAtlasName = Path.Combine(rf, sub).ESF_OnlyCNUAndOtherReplaceU().ToUpper();

                //    //string relativePath = bundleName.Replace(readSpritesRoot, "").ESF_ForceRelativePath();
                //    //string spriteAtlasNameWithoutExtension = $"{enESF_SpriteAtlasName}_{lan}";
                //    //string spriteAtlasName = spriteAtlasNameWithoutExtension + enESF_UnityFileExt.SpriteAtlas.ESF_FirstAttribute<ESF_FileExtAttribute>().ext;
                //    //string saveSpriteAtlasPath = Path.Combine(Path.Combine(writeSpriteAtlasRoot, relativePath), spriteAtlasName).ESF_TransPathSeparatorCharToUnityChar();

                //    importer.assetBundleName = OnResolveAssetBundleName(bundleName).ESF_TransPathSeparatorCharToUnityChar();
                //}
                EditorUtility.SetDirty(importer);
            }
        }        
        assetBundleName = importer.assetBundleName;
    }
    #endregion

    #region ClearAssetBundleName 清除AssetBundleName
    /// <summary>
    /// 清除AssetBundleName
    /// </summary>
    public void ClearAssetBundleName()
    {
        AssetImporter importer = path.ToAssetImporter();
        if (!string.IsNullOrEmpty(importer.assetBundleName))
        {
            importer.assetBundleVariant = string.Empty;
        }
        importer.assetBundleName = string.Empty;        
        EditorUtility.SetDirty(importer);
    }
    #endregion

    #region SaveLog 日志
    /// <summary>
    /// 日志
    /// </summary>
    /// <param name="_relativeSaveLogDirectoryForAssetBundle">相对于日志目录的资源包目录</param>
    /// <returns>保存文件地址</returns>
    public string SaveLog(string _relativeSaveLogDirectoryForAssetBundle)
    {
        StringBuilder sbLog = new StringBuilder();
        sbLog.AppendLine($"{path} Refrence by=> {mRefrenceNodes.Count}");
        sbLog.AppendLine(mRefrenceNodes.ESF_Join(enESF_SeparatorSymbol.NewLine));
        sbLog.AppendLine($"{path} Dependency by=> {mDependencyNodes.Count}");
        sbLog.AppendLine(mDependencyNodes.ESF_Join(enESF_SeparatorSymbol.NewLine));
        sbLog.ToString();
        return ESF_Utility.SaveLogFile(nameWithoutExtension, _relativeSaveLogDirectoryForAssetBundle, sbLog.ToString());
    }
    #endregion
}
#endif
